package com.cheng.im.service.group.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cheng.im.common.constants.SeqConstants;
import com.cheng.im.service.common.eunms.GroupErrorCode;
import com.cheng.im.service.common.eunms.GroupMemberRoleEnum;
import com.cheng.im.service.common.eunms.GroupStatusEnum;
import com.cheng.im.service.common.eunms.GroupTypeEnum;
import com.cheng.im.service.common.exception.ApplicationException;
import com.cheng.im.service.common.model.SyncReq;
import com.cheng.im.service.common.model.SyncResp;
import com.cheng.im.service.common.resp.CommonResult;
import com.cheng.im.service.group.domain.ImGroup;
import com.cheng.im.service.group.mapper.ImGroupMapper;
import com.cheng.im.service.group.model.req.*;
import com.cheng.im.service.group.model.resp.GetGroupResp;
import com.cheng.im.service.group.model.resp.GetJoinedGroupResp;
import com.cheng.im.service.group.model.resp.GetRoleInGroupResp;
import com.cheng.im.service.group.service.ImGroupMemberService;
import com.cheng.im.service.group.service.ImGroupService;
import com.cheng.im.service.redis.seq.RedisSeq;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
* @author Cheng
* @description 针对表【im_group】的数据库操作Service实现
* @createDate 2024-12-19 09:16:10
*/
@DubboService
public class ImGroupServiceImpl extends ServiceImpl<ImGroupMapper, ImGroup>
    implements ImGroupService {

    @Resource
    private ImGroupMemberService imGroupMemberService;
    @Resource
    RedisSeq redisSeq;
    @Resource
    ImGroupMemberService groupMemberService;

    @Override
    public CommonResult importGroup(ImportGroupReq req) {
        //1.判断群id是否存在
        LambdaQueryWrapper<ImGroup> query = new LambdaQueryWrapper<>();

        if (StringUtils.isEmpty(req.getGroupId())) {
            req.setGroupId(UUID.randomUUID().toString().replace("-", ""));
        } else {
            query.eq(ImGroup::getGroupId, req.getGroupId());
            query.eq(ImGroup::getAppId, req.getAppId());
            Long integer = baseMapper.selectCount(query);
            if (integer > 0) {
                throw new ApplicationException(GroupErrorCode.GROUP_IS_EXIST);
            }
        }

        ImGroup imGroupEntity = new ImGroup();

        if (req.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && StringUtils.isBlank(req.getOwnerId())) {
            throw new ApplicationException(GroupErrorCode.PUBLIC_GROUP_MUST_HAVE_OWNER);
        }

        if (req.getCreateTime() == null) {
            imGroupEntity.setCreateTime(System.currentTimeMillis());
        }
        imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode());
        BeanUtils.copyProperties(req, imGroupEntity);
        int insert = baseMapper.insert(imGroupEntity);

        if (insert != 1) {
            throw new ApplicationException(GroupErrorCode.IMPORT_GROUP_ERROR);
        }

        return CommonResult.success();
    }

    @Override
    @Transactional
    public CommonResult createGroup(CreateGroupReq req) {
        boolean isAdmin = false;

        if (!isAdmin) {
            req.setOwnerId(req.getOperator());
        }

        //1.判断群id是否存在
        LambdaQueryWrapper<ImGroup> query = new LambdaQueryWrapper<>();

        if (StringUtils.isEmpty(req.getGroupId())) {
            req.setGroupId(UUID.randomUUID().toString().replace("-", ""));
        } else {
            query.eq(ImGroup::getGroupId, req.getGroupId());
            query.eq(ImGroup::getAppId, req.getAppId());
            Long integer = baseMapper.selectCount(query);
            if (integer > 0) {
                throw new ApplicationException(GroupErrorCode.GROUP_IS_EXIST);
            }
        }

        if (req.getGroupType() == GroupTypeEnum.PUBLIC.getCode() && StringUtils.isBlank(req.getOwnerId())) {
            throw new ApplicationException(GroupErrorCode.PUBLIC_GROUP_MUST_HAVE_OWNER);
        }

        ImGroup imGroupEntity = new ImGroup();

        long seq = redisSeq.doGetSeq(req.getAppId() + ":" + SeqConstants.GROUP);
        imGroupEntity.setSequence(seq);
        imGroupEntity.setCreateTime(System.currentTimeMillis());
        imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode());
        BeanUtils.copyProperties(req, imGroupEntity);
        int insert = baseMapper.insert(imGroupEntity);

        GroupMemberDto groupMemberDto = new GroupMemberDto();
        groupMemberDto.setMemberId(req.getOwnerId());
        groupMemberDto.setRole(GroupMemberRoleEnum.OWNER.getCode());
        groupMemberDto.setJoinTime(System.currentTimeMillis());
        imGroupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), groupMemberDto);

        //插入群成员
        for (GroupMemberDto dto : req.getMember()) {
            imGroupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), dto);
        }

//        if(appConfig.isCreateGroupAfterCallback()){
//            callbackService.callback(req.getAppId(), Constants.CallbackCommand.CreateGroupAfter,
//                    JSONObject.toJSONString(imGroupEntity));
//        }
        // todo: 创建群后通知群用户
//        CreateGroupPack createGroupPack = new CreateGroupPack();
//        BeanUtils.copyProperties(imGroupEntity, createGroupPack);
//        groupMessageProducer.producer(req.getOperater(), GroupEventCommand.CREATED_GROUP, createGroupPack
//                , new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
        return CommonResult.success();
    }

    /**
     * 修改群基础信息，如果是后台管理员调用，则不检查权限，如果不是则检查权限，如果是私有群（微信群）任何人都可以修改资料，
     * 公开群只有管理员可以修改
     * 如果是群主或者管理员可以修改其他信息。
     * @param req
     * @return
     */
    @Override
    @Transactional
    public CommonResult updateBaseGroupInfo(UpdateGroupReq req) {
        //1.判断群id是否存在
        LambdaQueryWrapper<ImGroup> query = new LambdaQueryWrapper<>();
        query.eq(ImGroup::getGroupId, req.getGroupId());
        query.eq(ImGroup::getAppId, req.getAppId());
        ImGroup imGroupEntity = baseMapper.selectOne(query);
        if (imGroupEntity == null) {
            throw new ApplicationException(GroupErrorCode.GROUP_IS_EXIST);
        }

        if(imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()){
            throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        boolean isAdmin = false;

        if (!isAdmin) {
            //不是后台调用需要检查权限
            CommonResult<GetRoleInGroupResp> role = imGroupMemberService.getRoleInGroupOne(req.getGroupId(),
                    req.getOperator(), req.getAppId());

            if (!role.isOk()) {
                return role;
            }

            GetRoleInGroupResp data = role.getData();
            Integer roleInfo = data.getRole();

            boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode() || roleInfo == GroupMemberRoleEnum.OWNER.getCode();

            //公开群只能群主修改资料
            if (!isManager && GroupTypeEnum.PUBLIC.getCode() == imGroupEntity.getGroupType()) {
                throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
            }

        }

        ImGroup update = new ImGroup();
        long seq = redisSeq.doGetSeq(req.getAppId() + ":" + SeqConstants.GROUP);
        BeanUtils.copyProperties(req, update);
        update.setUpdateTime(System.currentTimeMillis());
        update.setSequence(seq);
        int row = baseMapper.update(update, query);
        if (row != 1) {
            throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
        }

//        if(appConfig.isModifyGroupAfterCallback()){
//            callbackService.callback(req.getAppId(),Constants.CallbackCommand.UpdateGroupAfter,
//                    JSONObject.toJSONString(imGroupDataMapper.selectOne(query)));
//        }
//        todo: 更新群后通知
//        UpdateGroupInfoPack pack = new UpdateGroupInfoPack();
//        BeanUtils.copyProperties(req, pack);
//        groupMessageProducer.producer(req.getOperater(), GroupEventCommand.UPDATED_GROUP,
//                pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));


        return CommonResult.success();
    }

    @Override
    public CommonResult getJoinedGroup(GetJoinedGroupReq req) {
        CommonResult<Collection<String>> memberJoinedGroup = imGroupMemberService.getMemberJoinedGroup(req);
        if (memberJoinedGroup.isOk()) {

            GetJoinedGroupResp resp = new GetJoinedGroupResp();

            if (CollectionUtils.isEmpty(memberJoinedGroup.getData())) {
                resp.setTotalCount(0L);
                resp.setGroupList(new ArrayList<>());
                return CommonResult.success(resp);
            }

            LambdaQueryWrapper<ImGroup> query = new LambdaQueryWrapper<>();
            query.eq(ImGroup::getAppId, req.getAppId());
            query.in(ImGroup::getGroupId, memberJoinedGroup.getData());

            if (CollectionUtils.isNotEmpty(req.getGroupType())) {
                query.in(ImGroup::getGroupType, req.getGroupType());
            }

            List<ImGroup> groupList = baseMapper.selectList(query);
            resp.setGroupList(groupList);
            if (req.getLimit() == null) {
                resp.setTotalCount((long) groupList.size());
            } else {
                resp.setTotalCount(baseMapper.selectCount(query));
            }
            return CommonResult.success(resp);
        } else {
            return memberJoinedGroup;
        }
    }

    /**
     * 解散群组，只支持后台管理员和群主解散
     * @param req
     * @return
     */
    @Override
    public CommonResult destroyGroup(DestroyGroupReq req) {
        boolean isAdmin = false;

        LambdaQueryWrapper<ImGroup> objectQueryWrapper = new LambdaQueryWrapper<>();
        objectQueryWrapper.eq(ImGroup::getGroupId, req.getGroupId());
        objectQueryWrapper.eq(ImGroup::getAppId, req.getAppId());
        ImGroup imGroupEntity = baseMapper.selectOne(objectQueryWrapper);
        if (imGroupEntity == null) {
            throw new ApplicationException(GroupErrorCode.PRIVATE_GROUP_CAN_NOT_DESTORY);
        }

        if(imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()){
            throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        if (!isAdmin) {
            if (imGroupEntity.getGroupType() == GroupTypeEnum.PUBLIC.getCode()) {
                throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE);
            }

            if (imGroupEntity.getGroupType() == GroupTypeEnum.PUBLIC.getCode() &&
                    !imGroupEntity.getOwnerId().equals(req.getOperator())) {
                throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE);
            }
        }

        ImGroup update = new ImGroup();
        long seq = redisSeq.doGetSeq(req.getAppId() + ":" + SeqConstants.GROUP);

        update.setStatus(GroupStatusEnum.DESTROY.getCode());
        update.setSequence(seq);
        int update1 = baseMapper.update(update, objectQueryWrapper);
        if (update1 != 1) {
            throw new ApplicationException(GroupErrorCode.UPDATE_GROUP_BASE_INFO_ERROR);
        }

//        if(appConfig.isModifyGroupAfterCallback()){
//            DestroyGroupCallbackDto dto = new DestroyGroupCallbackDto();
//            dto.setGroupId(req.getGroupId());
//            callbackService.callback(req.getAppId()
//                    ,Constants.CallbackCommand.DestoryGroupAfter,
//                    JSONObject.toJSONString(dto));
//        }
//          todo: 群聊解散通知
//        DestroyGroupPack pack = new DestroyGroupPack();
//        pack.setSequence(seq);
//        pack.setGroupId(req.getGroupId());
//        groupMessageProducer.producer(req.getOperater(),
//                GroupEventCommand.DESTROY_GROUP, pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));

        return CommonResult.success();
    }

    @Override
    public CommonResult transferGroup(TransferGroupReq req) {
        CommonResult<GetRoleInGroupResp> roleInGroupOne = imGroupMemberService.getRoleInGroupOne(req.getGroupId(),
                req.getOperator(), req.getAppId());
        if (!roleInGroupOne.isOk()) {
            return roleInGroupOne;
        }

        if (roleInGroupOne.getData().getRole() != GroupMemberRoleEnum.OWNER.getCode()) {
            return CommonResult.failed(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE.getCode(),
                    GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE.getError());
        }

        CommonResult<GetRoleInGroupResp> newOwnerRole = imGroupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOwnerId(), req.getAppId());
        if (!newOwnerRole.isOk()) {
            return newOwnerRole;
        }

        LambdaQueryWrapper<ImGroup> objectQueryWrapper = new LambdaQueryWrapper<>();
        objectQueryWrapper.eq(ImGroup::getGroupId, req.getGroupId());
        objectQueryWrapper.eq(ImGroup::getAppId, req.getAppId());
        ImGroup imGroupEntity = baseMapper.selectOne(objectQueryWrapper);
        if(imGroupEntity.getStatus() == GroupStatusEnum.DESTROY.getCode()){
            throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        ImGroup updateGroup = new ImGroup();
        updateGroup.setOwnerId(req.getOwnerId());
        UpdateWrapper<ImGroup> updateGroupWrapper = new UpdateWrapper<>();
        updateGroupWrapper.eq("app_id", req.getAppId());
        updateGroupWrapper.eq("group_id", req.getGroupId());
        baseMapper.update(updateGroup, updateGroupWrapper);
        imGroupMemberService.transferGroupMember(req.getOwnerId(), req.getGroupId(), req.getAppId());

        return CommonResult.success();
    }

    @Override
    public CommonResult<ImGroup> getGroup(String groupId, String appId) {
        LambdaQueryWrapper<ImGroup> query = new LambdaQueryWrapper<>();
        query.eq(ImGroup::getGroupId, groupId);
        query.eq(ImGroup::getAppId, appId);
        ImGroup imGroupEntity = baseMapper.selectOne(query);

        if (imGroupEntity == null) {
            return CommonResult.failed(GroupErrorCode.GROUP_IS_NOT_EXIST.getCode(),
                    GroupErrorCode.GROUP_IS_NOT_EXIST.getError());
        }
        return CommonResult.success(imGroupEntity);
    }

    @Override
    public CommonResult<GetGroupResp> getGroup(GetGroupReq req) {
        CommonResult group = this.getGroup(req.getGroupId(), req.getAppId());

        if(!group.isOk()){
            return group;
        }

        GetGroupResp getGroupResp = new GetGroupResp();
        BeanUtils.copyProperties(group.getData(), getGroupResp);
        try {
            CommonResult<List<GroupMemberDto>> groupMember = imGroupMemberService.getGroupMember(req.getGroupId(), req.getAppId());
            if (groupMember.isOk()) {
                getGroupResp.setMemberList(groupMember.getData());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return CommonResult.success(getGroupResp);
    }

    @Override
    public CommonResult muteGroup(MuteGroupReq req) {
        CommonResult<ImGroup> groupResp = getGroup(req.getGroupId(), req.getAppId());
        if (!groupResp.isOk()) {
            return groupResp;
        }

        if(groupResp.getData().getStatus() == GroupStatusEnum.DESTROY.getCode()){
            throw new ApplicationException(GroupErrorCode.GROUP_IS_DESTROY);
        }

        boolean isadmin = false;

        if (!isadmin) {
            //不是后台调用需要检查权限
            CommonResult<GetRoleInGroupResp> role = imGroupMemberService.getRoleInGroupOne(req.getGroupId(), req.getOperator(), req.getAppId());

            if (!role.isOk()) {
                return role;
            }

            GetRoleInGroupResp data = role.getData();
            Integer roleInfo = data.getRole();

            boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode() || roleInfo == GroupMemberRoleEnum.OWNER.getCode();

            //公开群只能群主修改资料
            if (!isManager) {
                throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
            }
        }

        ImGroup update = new ImGroup();
        update.setMute(req.getMute());

        UpdateWrapper<ImGroup> wrapper = new UpdateWrapper<>();
        wrapper.eq("group_id",req.getGroupId());
        wrapper.eq("app_id",req.getAppId());
        baseMapper.update(update,wrapper);

        return CommonResult.success();
    }

    @Override
    public CommonResult<SyncResp<ImGroup>> syncJoinedGroupList(SyncReq req) {
        if(req.getMaxLimit() > 100){
            req.setMaxLimit(100);
        }

        SyncResp<ImGroup> resp = new SyncResp<>();

        CommonResult<Collection<String>> memberJoinedGroup = groupMemberService.syncMemberJoinedGroup(req.getOperator(), req.getAppId());
        if(memberJoinedGroup.isOk()){

            Collection<String> data = memberJoinedGroup.getData();
            LambdaQueryWrapper<ImGroup> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ImGroup::getAppId, req.getAppId());
            queryWrapper.in(ImGroup::getGroupId, data);
            queryWrapper.gt(ImGroup::getSequence, req.getLastSequence());
            queryWrapper.last(" limit " + req.getMaxLimit());
            queryWrapper.orderByAsc(ImGroup::getSequence);

            List<ImGroup> list = baseMapper.selectList(queryWrapper);

            if(!CollectionUtils.isEmpty(list)){
                ImGroup maxSeqEntity
                        = list.get(list.size() - 1);
                resp.setDataList(list);
                //设置最大seq
                Long maxSeq =
                        baseMapper.getGroupMaxSeq(data, req.getAppId());
                resp.setMaxSequence(maxSeq);
                //设置是否拉取完毕
                resp.setCompleted(maxSeqEntity.getSequence() >= maxSeq);
                return CommonResult.success(resp);
            }

        }
        resp.setCompleted(true);
        return CommonResult.success(resp);
    }

    @Override
    public Long getUserGroupMaxSeq(String userId, String appId) {
        CommonResult<Collection<String>> memberJoinedGroup = groupMemberService.syncMemberJoinedGroup(userId, appId);
        if(!memberJoinedGroup.isOk()){
            throw new ApplicationException(500,"");
        }
        Long maxSeq = baseMapper.getGroupMaxSeq(memberJoinedGroup.getData(), appId);
        return maxSeq;
    }
}




